/*
 * Team A - Bookmanager
 * 
 */
package BookManager;

import java.io.*;

/**
 *
 * @author mif
 */


/* Main
 * 
 */
public class BookManager  {
    public static void main(String[] args) throws java.io.IOException {
        //int arraySize = 40277;
	//BookArray a;
        BookArray ba1, ba2, ba3, ba4, ba5; // Arrays for the importation procedure
        BookArray bs1, bs2, bs3, bs4, bs5; // these will be used for bubble sort
        BookArray ms1, ms2, ms3, ms4, ms5; // these will be used for merge sort
        BookArray is1, is2, is3, is4, is5; // these will be used for insertion sort
        Long start, end; // to measure start and end times in milliseconds;
	Book b;
        long mem;
        long mem0;
	long mem1;
	checkDataFiles();
	
	char sortChoice;
	do {
		System.out.println("Welcome to Library Catalog Sort!");
		System.out.println("First, please choose your book source:");
		System.out.println("   C - CSV Data Files (deterministic)");
		System.out.println("   R - Randomly generated (non-deterministic, funnier)");
		System.out.print("Choice --> ");
		sortChoice = getChar();
		sortChoice = Character.toUpperCase(sortChoice);
	}while(sortChoice != 'C' && sortChoice != 'R');
	
	if(sortChoice == 'C') {
	    ba1 = importCSVtoArray("books1.csv");//divide the imports into roughly equal sections
	    ba2 = importCSVtoArray("books2.csv");
	    ba3 = importCSVtoArray("books3.csv");
	    ba4 = importCSVtoArray("books4.csv");
	    ba5 = importCSVtoArray("books5.csv");
	}
	else {
	    RandomStringGenerator titles = new RandomStringGenerator("words.dat");
	    RandomStringGenerator authors = new RandomStringGenerator("names.dat");

	    // populate the book array with random strings
	    ba1 = new BookArray(10000);
	    for(int j = 0; j < 10000; j++) {
		b = new Book(titles.getRandomString() + " " + titles.getRandomString(), 
			authors.getRandomString() + ", " + authors.getRandomString(),
			RandomStringGenerator.GenerateString(5, 20),
			RandomStringGenerator.GenerateString(5, 20),
			RandomStringGenerator.GenerateString(5, 20)
			);
		ba1.add(b);
	    }
	    ba2 = new BookArray(10000);
	    for(int j = 0; j < 10000; j++) {
		b = new Book(titles.getRandomString() + " " + titles.getRandomString(), 
			authors.getRandomString() + ", " + authors.getRandomString(),
			RandomStringGenerator.GenerateString(5, 20),
			RandomStringGenerator.GenerateString(5, 20),
			RandomStringGenerator.GenerateString(5, 20)
			);
		ba2.add(b); //we should have used arrays for these
	    }
	    ba3 = new BookArray(10000);
	    for(int j = 0; j < 10000; j++) {
		b = new Book(titles.getRandomString() + " " + titles.getRandomString(), 
			authors.getRandomString() + ", " + authors.getRandomString(),
			RandomStringGenerator.GenerateString(5, 20),
			RandomStringGenerator.GenerateString(5, 20),
			RandomStringGenerator.GenerateString(5, 20)
			);
		ba3.add(b);
	    }
	    ba4 = new BookArray(10000);
	    for(int j = 0; j < 10000; j++) {
		b = new Book(titles.getRandomString() + " " + titles.getRandomString(), 
			authors.getRandomString() + ", " + authors.getRandomString(),
			RandomStringGenerator.GenerateString(5, 20),
			RandomStringGenerator.GenerateString(5, 20),
			RandomStringGenerator.GenerateString(5, 20)
			);
		ba4.add(b);
	    }
	    ba5 = new BookArray(10000);
	    for(int j = 0; j < 10000; j++) {
		b = new Book(titles.getRandomString() + " " + titles.getRandomString(), 
			authors.getRandomString() + ", " + authors.getRandomString(),
			RandomStringGenerator.GenerateString(5, 20),
			RandomStringGenerator.GenerateString(5, 20),
			RandomStringGenerator.GenerateString(5, 20)
			);
		ba5.add(b);
	    }

	}
	
	
        int iValue;
        int valueI;
        String sValue;
        BookArray theBooks = null;
	

       while(true)
         {
	 System.out.println("");
         System.out.println("** Library Catalog Sort **");
         System.out.println("          ");
         System.out.println("   A - All (All sorts, all sets)");
         System.out.println("   B - Bubble Sort");
         System.out.println("   I - Insertion Sort");
         System.out.println("   M - Merge Sort");
	 System.out.println("          ");
	 System.out.println("   R - Full report (all sorts, all sets, and individual listings)");
         System.out.println("          ");
	 System.out.println("   V - View BookArray (Warning: very large!)");                                                                                                                                                             System.out.println("   S - HO HO HO");
	 System.out.println("          ");

         System.out.println("   X - Exit");
         System.out.println("          ");
         System.out.print("Sort by: ");

         sortChoice = getChar();
	 sortChoice = Character.toUpperCase(sortChoice);
	 
	 System.out.print("Presorting and randomizing book list to ensure deterministic outcome.");
	ba1.mergeSort(); ba1.randomizeBooks(); System.out.print(".");   //presorting here ensures that repeated runs
	ba2.mergeSort(); ba2.randomizeBooks(); System.out.print(".");   //yield the same results
	ba3.mergeSort(); ba3.randomizeBooks(); System.out.print(".");   //mergeSort is the fastest for this data
	ba4.mergeSort(); ba4.randomizeBooks(); System.out.print("."); 
	ba5.mergeSort(); ba5.randomizeBooks(); System.out.println("."); 

	// create copies of the BookArray for each sort 
	//re-copying automatically frees the old memory in Java (yay for GC)
	bs1 = ba1.copy();  bs2 = ba2.copy(); bs3 = ba3.copy(); 
	bs4 = ba4.copy(); bs5 = ba5.copy(); // bs for Bubble Sort
	ms1 = ba1.copy();  ms2 = ba2.copy(); ms3 = ba3.copy(); 
	ms4 = ba4.copy(); ms5 = ba5.copy(); // ms for Merge Sort
	is1 = ba1.copy();  is2 = ba2.copy(); is3 = ba3.copy(); 
	is4 = ba4.copy(); is5 = ba5.copy(); // is for Insertion Sort

            switch(sortChoice) {
            case  'A':
		System.out.println("Sorting, please wait...");
                // MERGE SORT
	System.gc();
	mem = Runtime.getRuntime().freeMemory();
                start = System.currentTimeMillis();
                ms1.mergeSort();
                ms2.mergeSort();
                ms3.mergeSort();
                ms4.mergeSort();
                ms5.mergeSort();
                end = System.currentTimeMillis();
	mem = mem - Runtime.getRuntime().freeMemory();
                System.out.println("Time to sort (merge): " + (end - start) + " milliseconds, with "+ Globals.getString()+  " (" + mem + " bytes delta)");
	System.gc();
	mem0 = Runtime.getRuntime().freeMemory();
                // INSERTION SORT
                start = System.currentTimeMillis();
                is1.insertionSort();
                is2.insertionSort();
                is3.insertionSort();
                is4.insertionSort();
                is5.insertionSort();
                end = System.currentTimeMillis();
	mem0 = mem0 - Runtime.getRuntime().freeMemory();
                System.out.println("Time to sort (insertion): " + (end - start) + " milliseconds, with "+ Globals.getString()+ " (" +  mem0 + " bytes delta)");
	System.gc();
	mem1 = Runtime.getRuntime().freeMemory();
                // BUBBLE SORT
                start = System.currentTimeMillis();
                bs1.bubbleSort();
                bs2.bubbleSort();
                bs3.bubbleSort();
                bs4.bubbleSort();
                bs5.bubbleSort();
                end = System.currentTimeMillis();
	mem1 = mem1 - Runtime.getRuntime().freeMemory();
                System.out.println("Time to sort (bubble): " + (end - start) + " milliseconds, with "+ Globals.getString()+ " (" + mem1 + " bytes delta)");
	System.gc();
                break;
            case 'B':
               System.out.print("Enter Set 1, 2, 3, 4 or 5: ");
               valueI = getInt();
	       System.out.println("Sorting, please wait...");
               switch (valueI) {
                   case 1:
                       start = System.currentTimeMillis();
                       bs1.bubbleSort();
                       end = System.currentTimeMillis();
                       System.out.println("Time to sort (bubble): " + (end - start) + " milliseconds, with "+ Globals.getString());
                       break;
                   case 2: 
                       start = System.currentTimeMillis();
                       bs2.bubbleSort();
                       end = System.currentTimeMillis();
                       System.out.println("Time to sort (bubble): " + (end - start) + " milliseconds, with "+ Globals.getString());
                       break;
                   case 3:
                       start = System.currentTimeMillis();
                       bs3.bubbleSort();
                       end = System.currentTimeMillis();
                       System.out.println("Time to sort (bubble): " + (end - start) + " milliseconds, with "+ Globals.getString());
                       break;
                   case 4:
                       start = System.currentTimeMillis();
                       bs4.bubbleSort();
                       end = System.currentTimeMillis();
                       System.out.println("Time to sort (bubble): " + (end - start) + " milliseconds, with "+ Globals.getString());
                       break;
                   case 5:
                       start = System.currentTimeMillis();
                       bs5.bubbleSort();
                       end = System.currentTimeMillis();
                       System.out.println("Time to sort (bubble): " + (end - start) + " milliseconds, with "+ Globals.getString());
                       break;
                    }
                break;
                
            case 'I':
               System.out.print("Enter Set 1, 2, 3, 4 or 5: ");
               valueI = getInt();
	       System.out.println("Sorting, please wait...");
               switch (valueI) {
                   case 1:
                       start = System.currentTimeMillis();
                       is1.insertionSort();
                       end = System.currentTimeMillis();
                       System.out.println("Time to sort (insertion): " + (end - start) + " milliseconds, with "+ Globals.getString());
                       break;
                   case 2: 
                       start = System.currentTimeMillis();
                       is2.insertionSort();
                       end = System.currentTimeMillis();
                       System.out.println("Time to sort (insertion): " + (end - start) + " milliseconds, with "+ Globals.getString());
                       break;
                   case 3:
                       start = System.currentTimeMillis();
                       is3.insertionSort();
                       end = System.currentTimeMillis();
                       System.out.println("Time to sort (insertion): " + (end - start) + " milliseconds, with "+ Globals.getString());
                       break;
                   case 4:
                       start = System.currentTimeMillis();
                       is4.insertionSort();
                       end = System.currentTimeMillis();
                       System.out.println("Time to sort (insertion): " + (end - start) + " milliseconds, with "+ Globals.getString());
                       break;
                   case 5:
                       start = System.currentTimeMillis();
                       is5.insertionSort();
                       end = System.currentTimeMillis();
                       System.out.println("Time to sort (insertion): " + (end - start) + " milliseconds, with "+ Globals.getString());
                       break;
                    }
                break;
            case 'M':
               System.out.print("Enter Set 1, 2, 3, 4 or 5: ");
               valueI = getInt();
	       System.out.println("Sorting, please wait...");
               switch (valueI) {
                   case 1:
                       start = System.currentTimeMillis();
                       ms1.mergeSort();
                       end = System.currentTimeMillis();
                       System.out.println("Time to sort (merge): " + (end - start) + " milliseconds, with "+ Globals.getString());
                       break;
                   case 2: 
                       start = System.currentTimeMillis();
                       ms2.mergeSort();
                       end = System.currentTimeMillis();
                       System.out.println("Time to sort (merge): " + (end - start) + " milliseconds, with "+ Globals.getString());
                       break;
                   case 3:
                       start = System.currentTimeMillis();
                       ms3.mergeSort();
                       end = System.currentTimeMillis();
                       System.out.println("Time to sort (merge): " + (end - start) + " milliseconds, with "+ Globals.getString());
                       break;
                   case 4:
                       start = System.currentTimeMillis();
                       ms4.mergeSort();
                       end = System.currentTimeMillis();
                       System.out.println("Time to sort (merge): " + (end - start) + " milliseconds, with "+ Globals.getString());
                       break;
                   case 5:
                       start = System.currentTimeMillis();
                       ms5.mergeSort();
                       end = System.currentTimeMillis();
                       System.out.println("Time to sort (merge): " + (end - start) + " milliseconds, with "+ Globals.getString());
                       break;
                    }
                break;
	    case 'V': 
		System.out.println("Import 1 (may take a while to load):");
		System.out.println(ba1.toString());
		System.out.println("Import 2 (may take a while to load):");
		System.out.println(ba2.toString());
		System.out.println("Import 3 (may take a while to load):");
		System.out.println(ba3.toString());
		System.out.println("Import 4 (may take a while to load):");
		System.out.println(ba4.toString());
		System.out.println("Import 5 (may take a while to load):");
		System.out.println(ba5.toString());
		break;
	    case 'R':
		long startTotal = System.currentTimeMillis();
		long actionsTotal = 0;
		long startMerge = System.currentTimeMillis();
		long mergeActions = 0;
		System.out.println("Starting sort performance report: ");
		start = System.currentTimeMillis();
		    ms1.mergeSort();
		    end = System.currentTimeMillis();
		    System.out.println("Time to sort 1 (merge): " + (end - start) + " milliseconds, with "+ Globals.getString());
		    mergeActions += Globals.Actions;
		start = System.currentTimeMillis();
		    ms2.mergeSort();
		    end = System.currentTimeMillis();
		    System.out.println("Time to sort 2 (merge): " + (end - start) + " milliseconds, with "+ Globals.getString());
		    mergeActions += Globals.Actions;
		start = System.currentTimeMillis();
		    ms3.mergeSort();
		    end = System.currentTimeMillis();
		    System.out.println("Time to sort 3 (merge): " + (end - start) + " milliseconds, with "+ Globals.getString());
		    mergeActions += Globals.Actions;
		start = System.currentTimeMillis();
		    ms4.mergeSort();
		    end = System.currentTimeMillis();
		    System.out.println("Time to sort 4 (merge): " + (end - start) + " milliseconds, with "+ Globals.getString());
		    mergeActions += Globals.Actions;
		start = System.currentTimeMillis();
		    ms5.mergeSort();
		    end = System.currentTimeMillis();
		    System.out.println("Time to sort 5 (merge): " + (end - start) + " milliseconds, with "+ Globals.getString());
		    mergeActions += Globals.Actions;
		
		System.out.println("Time to sort all by merge: " + (end - startMerge) + " milliseconds, with "+mergeActions + " actions.");
		
		actionsTotal += mergeActions;
		
		long startBubble = System.currentTimeMillis();
		long bubbleActions = 0;
		start = System.currentTimeMillis();
		    bs1.bubbleSort();
		    end = System.currentTimeMillis();
		    System.out.println("Time to sort 1 (bubble): " + (end - start) + " milliseconds, with "+ Globals.getString());
		    bubbleActions += Globals.Actions;
		start = System.currentTimeMillis();
		    bs2.bubbleSort();
		    end = System.currentTimeMillis();
		    System.out.println("Time to sort 2 (bubble): " + (end - start) + " milliseconds, with "+ Globals.getString());
		    bubbleActions += Globals.Actions;
		start = System.currentTimeMillis();
		    bs3.bubbleSort();
		    end = System.currentTimeMillis();
		    System.out.println("Time to sort 3 (bubble): " + (end - start) + " milliseconds, with "+ Globals.getString());
		    bubbleActions += Globals.Actions;
		start = System.currentTimeMillis();
		    bs4.bubbleSort();
		    end = System.currentTimeMillis();
		    System.out.println("Time to sort 4 (bubble): " + (end - start) + " milliseconds, with "+ Globals.getString());
		    bubbleActions += Globals.Actions;
		start = System.currentTimeMillis();
		    bs5.bubbleSort();
		    end = System.currentTimeMillis();
		    System.out.println("Time to sort 5 (bubble): " + (end - start) + " milliseconds, with "+ Globals.getString());
		    bubbleActions += Globals.Actions;
		
		System.out.println("Time to sort all by bubble: " + (end - startBubble) + " milliseconds, with "+bubbleActions + " actions.");
		
		actionsTotal += bubbleActions;
		
		long startInsertion = System.currentTimeMillis();
		long insertionActions = 0;
		start = System.currentTimeMillis();
		    is1.insertionSort();
		    end = System.currentTimeMillis();
		    System.out.println("Time to sort 1 (insertion): " + (end - start) + " milliseconds, with "+ Globals.getString());
		    insertionActions += Globals.Actions;
		start = System.currentTimeMillis();
		    is2.insertionSort();
		    end = System.currentTimeMillis();
		    System.out.println("Time to sort 2 (insertion): " + (end - start) + " milliseconds, with "+ Globals.getString());
		    insertionActions += Globals.Actions;
		start = System.currentTimeMillis();
		    is3.insertionSort();
		    end = System.currentTimeMillis();
		    System.out.println("Time to sort 3 (insertion): " + (end - start) + " milliseconds, with "+ Globals.getString());
		    insertionActions += Globals.Actions;
		start = System.currentTimeMillis();
		    is4.insertionSort();
		    end = System.currentTimeMillis();
		    System.out.println("Time to sort 4 (insertion): " + (end - start) + " milliseconds, with "+ Globals.getString());
		    insertionActions += Globals.Actions;
		start = System.currentTimeMillis();
		    is5.insertionSort();
		    end = System.currentTimeMillis();
		    System.out.println("Time to sort 5 (insertion): " + (end - start) + " milliseconds, with "+ Globals.getString());
		    insertionActions += Globals.Actions;
		
		System.out.println("Time to sort all by insertion: " + (end - startInsertion) + " milliseconds, with "+insertionActions + " actions.");
		
		actionsTotal += insertionActions;
		System.out.println("Total time to sort all sets by all methods: " + (end - startTotal) + " milliseconds, with "+ actionsTotal + " actions.");
		System.out.println("End sort performance report.");
		break;		
            case 'X':
	       System.exit(0);
	       break;                                                                                                                                                                                                                                                                                   case 'S': System.out.println("      /\\\n     /\\\\\\\n    //\\/\\\\\n      ||\n   ho ho ho\n   Congrats\n     and\n   Well Done\n  Team Alpha!"); for(int q=0; q< 8;q++){System.out.print("* "); try{Thread.sleep(500);}catch(Exception e) {};} break;
            default:
               System.out.print("Invalid entry\n");
	   }

         }
    }
   
    /*Code inherited from Lafore
     * 
     */
   public static String getString() throws IOException
      {
      InputStreamReader isr = new InputStreamReader(System.in);
      BufferedReader br = new BufferedReader(isr);
      String s = br.readLine();
      return s;
      }
    // -------------------------------------------------------------
   public static char getChar() throws IOException
      {
      String s = getString();
      return s.charAt(0);
      }
    //-------------------------------------------------------------
   public static int getInt() throws IOException
      {
      String s = getString();
      return Integer.parseInt(s);
      }
    // -------------------------------------------------------------
    public static BookArray importCSVtoArray(String filename) throws IOException {
        Book b;
        BookArray a = new BookArray(100);
        BufferedReader BookFile = 
	    new BufferedReader(new FileReader(filename));
	int counter = 0;
	String bookData = null;
	
	System.out.println("Importing book list from " + filename );
	
	bookData = BookFile.readLine(); //read first line	

	while (bookData != null){  // exits 
            String[] bookFields = bookData.split(","); // reads text to array. Divided by comma
	    //System.out.println(bookFields[0] + " " + bookFields.length);
	    if(bookFields.length < 5) { //double-check that we have enough columns
		bookData = BookFile.readLine(); // Read next line.
		continue;
	    }
            b = new Book(bookFields[0], bookFields[1], bookFields[2], bookFields[4], bookFields[3]);
            a.add(b);
                
	    counter++;
            //System.out.println(); // Print the line.
	    bookData = BookFile.readLine(); // Read next line.
	}
	BookFile.close();     // Close the file 

	System.out.println("Imported " + counter + " lines.");  // display
        return a;
   }
    
    public static void checkDataFiles() { //checks for all data files and bails if they're missing
	String fileNames[] = {"books1.csv", "books2.csv", "books3.csv", 
			   "books4.csv", "books5.csv","names.dat", "words.dat"};
	int c = 0;
		
	for(String fn: fileNames) {
	    java.io.File f = new java.io.File(fn);
	    if(! f.exists()) {
		System.out.println("Missing file: " + fn);
		c++;
	    }	
	}
	if(c > 0) {
	    System.out.println("Error: missing data files.");
	    System.out.println("Please check that the listed files are in the current working directory!");
	    System.out.println("(If running from a .JAR, please extract the files using any unzip");
	    System.out.println("utility before executing.)");
	    System.exit(1);
	}
    }
}

